import { watch } from 'vue';
import { ComponentBase, GenerateComponent } from '../component-base';
import { Util, calendar, defaultLunar, defaultGregorian, todayString, isBrowser, IParam } from 'ibz-core';

export class AppCalendarProps {
  /**
   * @description 多选
   * @type {boolean}
   * @memberof AppCalendarProps
   */
  multi: boolean = false;

  /**
   * @description 图标
   * @type {IParam[]}
   * @memberof AppCalendarProps
   */
  illustration: IParam[] = [];

  /**
   * @description 长度
   * @type {number}
   * @memberof AppCalendarProps
   */
  touchLength: number = 0;

  /**
   * @description 左箭头
   * @type {string}
   * @memberof AppCalendarProps
   */
  arrowLeft: string = '';

  /**
   * @description 右箭头
   * @type {string}
   * @memberof AppCalendarProps
   */
  arrowRight: string = '';

  /**
   * @description 清除
   * @type {boolean}
   * @memberof AppCalendarProps
   */
  clean: boolean = false;

  /**
   * @description 非现在
   * @type {boolean}
   * @memberof AppCalendarProps
   */
  noNow: boolean = false;

  /**
   * @description 是否有范围
   * @type {boolean}
   * @memberof AppCalendarProps
   */
  range: boolean = false;

  /**
   * @description 是否完成
   * @type {boolean}
   * @memberof AppCalendarProps
   */
  completion: boolean = false;

  /**
   * @description 当前值
   * @type {any[]}
   * @memberof AppCalendarProps
   */
  value: any[] = [];

  /**
   * @description 开始
   * @type {string[]}
   * @memberof AppCalendarProps
   */
  begin: string[] = [];

  /**
   * @description 结束
   * @type {string[]}
   * @memberof AppCalendarProps
   */
  end: string[] = [];

  /**
   * @description 是否补零
   * @type {boolean}
   * @memberof AppCalendarProps
   */
  zero: boolean = false;

  /**
   * @description 禁用
   * @type {IParam[]}
   * @memberof AppCalendarProps
   */
  disabled: IParam[] = [];

  /**
   * @description 本年历
   * @type {*}
   * @memberof AppCalendarProps
   */
  almanacs: IParam = {};

  /**
   * @description 内容
   * @type {IParam[]}
   * @memberof AppCalendarProps
   */
  tileContent: IParam[] = [];

  /**
   * @description 标记
   * @type {IParam[]}
   * @memberof AppCalendarProps
   */
  sign: IParam[] = [];

  /**
   * @description 是否为阴历
   * @type {boolean}
   * @memberof AppCalendarProps
   */
  lunar: boolean = false;

  /**
   * @description 是否周一开始
   * @type {boolean}
   * @memberof AppCalendarProps
   */
  monFirst: boolean = false;

  /**
   * @description 周显示
   * @type {string[]}
   * @memberof AppCalendarProps
   */
  weeks: string[] = [];

  /**
   * @description 月显示
   * @type {string[]}
   * @memberof AppCalendarProps
   */
  months: string[] = [];

  /**
   * @description 事程
   * @type {IParam}
   * @memberof AppCalendarProps
   */
  events: IParam = {};

  /**
   * @description 是否改变样式
   * @type {boolean}
   * @memberof AppCalendarProps
   */
  isChangeStyle: boolean = false;

  /**
   * @description 是否开启周开关
   * @type {boolean}
   * @memberof AppCalendarProps
   */
  weekSwitch: boolean = false;

  /**
   * @description 月范围
   * @type {any[]}
   * @memberof AppCalendarProps
   */
  monthRange: any[] = [];

  /**
   * @description 是否自适应
   * @type {boolean}
   * @memberof AppCalendarProps
   */
  responsive: boolean = false;

  /**
   * @description 月范围格式
   * @type {string}
   * @memberof AppCalendarProps
   */
  rangeMonthFormat: string = '';
}
export class AppCalendar extends ComponentBase<AppCalendarProps> {
  /**
   * @description 多选
   * @type {boolean}
   * @memberof AppCalendar
   */
  multi: boolean = false;

  /**
   * @description 图标
   * @type {IParam[]}
   * @memberof AppCalendar
   */
  illustration: IParam[] = [];

  /**
   * @description 长度
   * @type {number}
   * @memberof AppCalendar
   */
  touchLength: number = 0;

  /**
   * @description 左箭头
   * @type {string}
   * @memberof AppCalendar
   */
  arrowLeft: string = '';

  /**
   * @description 右箭头
   * @type {string}
   * @memberof AppCalendar
   */
  arrowRight: string = '';

  /**
   * @description 清除
   * @type {boolean}
   * @memberof AppCalendar
   */
  clean: boolean = false;

  /**
   * @description 现在
   * @type {boolean}
   * @memberof AppCalendar
   */
  now: boolean = true;

  /**
   * @description 是否有范围
   * @type {boolean}
   * @memberof AppCalendar
   */
  range: boolean = false;

  /**
   * @description 是否完成
   * @type {boolean}
   * @memberof AppCalendar
   */
  completion: boolean = false;

  /**
   * @description 当前值
   * @type {any[]}
   * @memberof AppCalendar
   */
  value: any[] = [];

  /**
   * @description 开始
   * @type {string[]}
   * @memberof AppCalendar
   */
  begin: string[] = [];

  /**
   * @description 结束
   * @type {string[]}
   * @memberof AppCalendar
   */
  end: string[] = [];

  /**
   * @description 是否补零
   * @type {boolean}
   * @memberof AppCalendar
   */
  zero: boolean = false;

  /**
   * @description 禁用
   * @type {any[]}
   * @memberof AppCalendar
   */
  disabled: IParam[] = [];

  /**
   * @description 本年历
   * @type {*}
   * @memberof AppCalendar
   */
  almanacs: IParam = {};

  /**
   * @description 内容
   * @type {any[]}
   * @memberof AppCalendar
   */
  tileContent: IParam[] = [];

  /**
   * @description 标记
   * @type {IParam[]}
   * @memberof AppCalendar
   */
  sign: IParam[] = [];

  /**
   * @description 是否为阴历
   * @type {boolean}
   * @memberof AppCalendar
   */
  lunar: boolean = false;

  /**
   * @description 是否周一开始
   * @type {boolean}
   * @memberof AppCalendar
   */
  monFirst: boolean = false;

  /**
   * @description 周显示
   * @type {string[]}
   * @memberof AppCalendar
   */
  weeks: string[] = [];

  /**
   * @description 月显示
   * @type {string[]}
   * @memberof AppCalendar
   */
  months: string[] = [];

  /**
   * @description 事程
   * @type {*}
   * @memberof AppCalendar
   */
  events: IParam = {};

  /**
   * @description 是否改变样式
   * @type {boolean}
   * @memberof AppCalendar
   */
  isChangeStyle: boolean = false;

  /**
   * @description 是否开启周开关
   * @type {boolean}
   * @memberof AppCalendar
   */
  weekSwitch: boolean = false;

  /**
   * @description 月范围
   * @type {any[]}
   * @memberof AppCalendar
   */
  monthRange: any[] = [];

  /**
   * @description 是否自适应
   * @type {boolean}
   * @memberof AppCalendar
   */
  responsive: boolean = false;

  /**
   * @description 月范围格式
   * @type {string}
   * @memberof AppCalendar
   */
  rangeMonthFormat: string = '';

  /**
   * @description 改变按钮
   * @type {boolean}
   * @memberof AppCalendar
   */
  changebtntop: boolean = true;

  /**
   * @description 年集合
   * @type {any[]}
   * @memberof AppCalendar
   */
  years: number[] = [];

  /**
   * @description 是否显示年集合
   * @type {boolean}
   * @memberof AppCalendar
   */
  yearsShow: boolean = false;

  /**
   * @description 年份
   * @type {number}
   * @memberof AppCalendar
   */
  year: number = 0;

  /**
   * @description 月份
   * @type {number}
   * @memberof AppCalendar
   */
  month: number = 0;

  /**
   * @description 月位置
   * @type {number}
   * @memberof AppCalendar
   */
  monthPosition: number = 0;

  /**
   * @description 天
   * @type {number}
   * @memberof AppCalendar
   */
  day: number = 0;

  /**
   * @description 天集合
   * @type {any[]}
   * @memberof AppCalendar
   */
  days: IParam[] = [];

  /**
   * @description 多选天
   * @type {any[]}
   * @memberof AppCalendar
   */
  multiDays: IParam[] = [];

  /**
   * @description 今天集合
   * @type {any[]}
   * @memberof AppCalendar
   */
  today: number[] = [];

  /**
   * @description 处理多选天数据集合
   * @type {any[]}
   * @memberof AppCalendar
   */
  handleMultiDay: IParam[] = [];

  /**
   * @description 是否第一次绘制
   * @type {boolean}
   * @memberof AppCalendar
   */
  firstRender: boolean = true;

  /**
   * @description 是否为Ios
   * @type {boolean}
   * @memberof AppCalendar
   */
  isIos: boolean = true;

  /**
   * @description 显示今天
   * @type {*}
   * @memberof AppCalendar
   */
  showToday: IParam = {};

  /**
   * @description 月文本
   * @type {string}
   * @memberof AppCalendar
   */
  monthText: string = '';

  /**
   * @description 节日
   * @type {*}
   * @memberof AppCalendar
   */
  festival: IParam = {
    lunar: defaultLunar,
    gregorian: defaultGregorian,
  };

  /**
   * @description 开始范围
   * @type {any[]}
   * @memberof AppCalendar
   */
  rangeBegin: number[] = [];

  /**
   * @description 结束范围
   * @type {any[]}
   * @memberof AppCalendar
   */
  rangeEnd: number[] = [];

  /**
   * @description 多选天数据
   * @type {any[]}
   * @memberof AppCalendar
   */
  multiDaysData: IParam[] = [];

  /**
   * @description 月循环
   * @type {any[]}
   * @memberof AppCalendar
   */
  monthsLoop: string[] = [];

  /**
   * @description 项宽
   * @type {number}
   * @memberof AppCalendar
   */
  itemWidth: number = 50;

  /**
   * @description 单位
   * @type {string}
   * @memberof AppCalendar
   */
  unit: string = 'rem';

  /**
   * @description 位置高度
   * @type {number}
   * @memberof AppCalendar
   */
  positionH: number = -0.48;

  /**
   * @description 月索引
   * @type {number}
   * @memberof AppCalendar
   */
  monthIndex: number = 0;

  /**
   * @description 是否越过
   * @type {boolean}
   * @memberof AppCalendar
   */
  oversliding: boolean = false;

  /**
   * @description 是否高低差
   * @type {boolean}
   * @memberof AppCalendar
   */
  rangeBgHide: boolean = false;

  /**
   * @description 月范围天数
   * @type {any[]}
   * @memberof AppCalendar
   */
  monthRangeDays: any[] = [];

  /**
   * @description 月范围
   * @type {any[]}
   * @memberof AppCalendar
   */
  rangeOfMonths: any[] = [];

  /**
   * @description 月天数
   * @type {any[]}
   * @memberof AppCalendar
   */
  monthDays: IParam[] = [];

  /**
   * @description 周索引
   * @type {number}
   * @memberof AppCalendar
   */
  weekIndex: number = 0;

  /**
   * @description 开始周索引
   * @type {number}
   * @memberof AppCalendar
   */
  startWeekIndex: number = 0;

  /**
   * @description 周位置
   * @type {boolean}
   * @memberof AppCalendar
   */
  positionWeek: boolean = true;

  /**
   * @description 是否在月范围
   * @type {boolean}
   * @memberof AppCalendar
   */
  isMonthRange: boolean = false;

  /**
   * @description 是否选中
   * @type {boolean}
   * @memberof AppCalendar
   */
  isseletd: boolean = false;

  /**
   * @description 月循环副本
   * @type {string[]}
   * @memberof AppCalendar
   */
  monthsLoopCopy: string[] = [];

  /**
   * @description 是否初始化绘制
   * @type {boolean}
   * @memberof AppCalendar
   */
  initRender: boolean = false;

  /**
   * @description 当前时间选择
   * @type {*}
   * @memberof AppCalendar
   */
  thisTimeSelect: number[] = [];

  /**
   * @description 是否为用户选择
   * @type {boolean}
   * @memberof AppCalendar
   */
  isUserSelect: boolean = false;

  /**
   * @description 临时开始时间范围
   * @type {number[]}
   * @memberof AppCalendar
   */
  rangeBeginTemp: number[] = [];

  /**
   * @description 临时结束时间范围
   * @type {number}
   * @memberof AppCalendar
   */
  rangeEndTemp: number = 0;

  /**
   * @description 设置响应式
   * @memberof AppCalendar
   */
  setup() {
    this.initInputData(this.props);
    this.setCalendarWatch();
    super.setup();
  }

  /**
   * @description 值属性变更
   * @memberof AppCalendar
   */
  watchEffect() {
    this.initInputData(this.props);
  }

  /**
   * @description 初始化输入属性
   * @param {IParam} opts 输入参数
   * @memberof AppCalendar
   */
  initInputData(opts: AppCalendarProps) {
    this.multi = !!opts.multi;
    this.illustration = opts.illustration ? opts.illustration : [];
    this.touchLength = opts.touchLength ? opts.touchLength : 0;
    this.arrowLeft = opts.arrowLeft ? opts.arrowLeft : '';
    this.arrowRight = opts.arrowRight ? opts.arrowRight : '';
    this.clean = !!opts.clean;
    this.now = opts.noNow ? false : true;
    this.range = !!opts.range;
    this.completion = !!opts.completion;
    this.value = opts.value ? opts.value : [];
    this.begin = opts.begin ? opts.begin : [];
    this.end = opts.end ? opts.end : [];
    this.zero = !!opts.zero;
    this.disabled = opts.disabled ? opts.disabled : [];
    this.almanacs = opts.almanacs ? opts.almanacs : {};
    this.tileContent = opts.tileContent ? opts.tileContent : [];
    this.sign = opts.sign ? opts.sign : [];
    this.lunar = !!opts.lunar;
    this.monFirst = !!opts.monFirst;
    this.weeks = opts.weeks ? opts.months : this.weeks;
    this.months = opts.months ? opts.months : this.months;
    this.events = opts.events ? opts.events : {};
    this.isChangeStyle = !!opts.isChangeStyle;
    this.weekSwitch = !!opts.weekSwitch;
    this.monthRange = opts.monthRange ? opts.monthRange : [];
    this.responsive = !!opts.responsive;
    this.rangeMonthFormat = opts.rangeMonthFormat ? opts.rangeMonthFormat : '';
  }

  /**
   * @description 日历组件基础数据初始化
   * @memberof AppCalendar
   */
  calendarBasicInit() {
    this.isMonthRange = !!this.monthRange?.length;
    const loopArray: any[] = this.months?.concat() || [];
    if (this.months) {
      loopArray?.unshift(this.months[this.months.length - 1]);
      loopArray.push(this.months[0]);
    }
    this.monthsLoop = loopArray;
    this.monthsLoopCopy = this.monthsLoop.concat();
  }

  /**
   * @description 设置日历监听
   * @memberof AppCalendar
   */
  setCalendarWatch() {
    watch(
      () => this.weekSwitch,
      () => {
        this.weekSwitchChange();
      },
    );
    watch(
      () => this.responsive,
      () => {
        this.responsiveChange();
      },
    );
    watch(
      () => this.monthRange,
      () => {
        this.monthRangeChange();
      },
    );
    watch(
      () => this.almanacs,
      () => {
        this.almanacsChange();
      },
    );
    watch(
      () => this.tileContent,
      () => {
        this.tileContentChange();
      },
    );
    watch(
      () => this.value,
      () => {
        this.valueChange();
      },
    );
    watch(
      () => this.disabled,
      () => {
        this.disabledChange();
      },
    );
    watch(
      () => this.events,
      () => {
        this.eventsChange();
      },
    );
  }

  /**
   * @description 周开关变化
   * @return {*}
   * @memberof AppCalendar
   */
  weekSwitchChange() {
    if (this.isRendeRangeMode()) return;
    this.initRenderParam(this.year, this.month, '_WATCHRENDER_', 'almanacs');
  }

  /**
   * @description 自适应变化
   * @memberof AppCalendar
   */
  responsiveChange() {
    if (this.responsive) this.addResponsiveListener();
  }

  /**
   * @description 月范围变化
   * @return {*}
   * @memberof AppCalendar
   */
  monthRangeChange() {
    if (this.isRendeRangeMode()) return;
    this.initRenderParam(this.year, this.month, '_WATCHRENDER_', 'almanacs');
  }

  /**
   * @description 年历变化
   * @return {*}
   * @memberof AppCalendar
   */
  almanacsChange() {
    if (this.isRendeRangeMode()) return;
    this.initRenderParam(this.year, this.month, '_WATCHRENDER_', 'almanacs');
  }

  /**
   * @description 内容变化
   * @return {*}
   * @memberof AppCalendar
   */
  tileContentChange() {
    if (this.isRendeRangeMode()) return;
    this.initRenderParam(this.year, this.month, '_WATCHRENDER_', 'tileContent');
  }

  /**
   * @description 当前值变化
   * @return {*}
   * @memberof AppCalendar
   */
  valueChange() {
    if (this.isRendeRangeMode('_WATCHRENDERVALUE_')) return;
    const value = this.value;
    let year = value[0] || this.year;
    let month = value[1] - 1 || this.month;
    let day;
    if (this.multi) {
      if (this.isUserSelect) {
        year = this.year;
        month = this.month;
        this.isUserSelect = false;
      } else {
        year = (value[value.length - 1] || [])[0] || this.year;
        month = (value[value.length - 1] || [])[1] - 1 || this.month;
      }
    } else if (this.range) {
      if (this.isUserSelect) {
        year = this.year;
        month = this.month;
        this.isUserSelect = false;
      } else {
        if (value.length) {
          year = value[0][0];
          month = value[0][1] - 1;
          day = value[0][2];
        }
        return this.initRenderParam(year, month, '_WATCHRENDERVALUE_', [year, month, day]);
      }
    }
    this.initRenderParam(year, month, '_WATCHRENDERVALUE_');
  }

  /**
   * @description 禁用变化
   * @return {*}
   * @memberof AppCalendar
   */
  disabledChange() {
    if (this.isRendeRangeMode()) return;
    this.initRenderParam(this.year, this.month, '_WATCHRENDER_', 'disabled');
  }

  /**
   * @description 事程变化
   * @return {*}
   * @memberof AppCalendar
   */
  eventsChange() {
    if (this.isRendeRangeMode()) return;
    this.initRenderParam(this.year, this.month, '_WATCHRENDER_', 'events');
  }

  /**
   * @description 组件挂载
   * @memberof AppCalendar
   */
  mounted() {
    this.initCalendarShow();
    this.calendarBasicInit();
    this.resize();
    if (!isBrowser) {
      // TODO
    } else if (this.responsive) {
      this.addResponsiveListener();
    }
    this.oversliding = true;
    this.initRender = true;
    this.afterMounted();
  }

  /**
   * @description 初始化日历显示
   * @memberof AppCalendar
   */
  initCalendarShow() {
    const weeks = [
      `${this.$tl('common.calendar.Sunday', '日')}`,
      `${this.$tl('common.calendar.Monday', '一')}`,
      `${this.$tl('common.calendar.Tuesday', '二')}`,
      `${this.$tl('common.calendar.Wednesday', '三')}`,
      `${this.$tl('common.calendar.Thursday', '四')}`,
      `${this.$tl('common.calendar.Friday', '五')}`,
      `${this.$tl('common.calendar.Saturday', '六')}`,
    ];
    if (this.monFirst) {
      const Sunday = weeks[0];
      weeks.splice(0, 1).push(Sunday);
    }
    this.weeks = weeks;
    const months = [
      `${this.$tl('common.calendar.January', '一月')}`,
      `${this.$tl('common.calendar.February', '二月')}`,
      `${this.$tl('common.calendar.March', '三月')}`,
      `${this.$tl('common.calendar.April', '四月')}`,
      `${this.$tl('common.calendar.May', '五月')}`,
      `${this.$tl('common.calendar.June', '六月')}`,
      `${this.$tl('common.calendar.July', '七月')}`,
      `${this.$tl('common.calendar.August', '八月')}`,
      `${this.$tl('common.calendar.September', '九月')}`,
      `${this.$tl('common.calendar.October', '十月')}`,
      `${this.$tl('common.calendar.November', '十一月')}`,
      `${this.$tl('common.calendar.December', '十二月')}`,
    ];
    this.months = months;
  }

  /**
   * @description 挂载完成之后
   * @return {*}
   * @memberof AppCalendar
   */
  afterMounted() {
    const now = new Date();
    this.year = now.getFullYear();
    this.month = now.getMonth();
    this.day = now.getDate();
    this.monthIndex = this.month + 1;
    if (this.value?.length || this.multi) {
      if (this.range) {
        this.year = Number(this.value[0][0]);
        this.month = this.value[0][1] - 1;
        this.day = Number(this.value[0][2]);
        const yearEnd = Number(this.value[1][0]);
        const monthEnd = this.value[1][1] - 1;
        const dayEnd = this.value[1][2];
        this.rangeBegin = [this.year, this.month, this.day];
        this.rangeEnd = [yearEnd, monthEnd, dayEnd];
      } else if (this.multi) {
        this.multiDays = this.value;
        const { handleMultiDay } = this;
        if (this.firstRender) {
          this.firstRender = false;
          const thatYear = (this.value[0] || [])[0];
          const thatMonth = (this.value[0] || [])[1];
          if (isFinite(thatYear) && isFinite(thatMonth)) {
            this.month = parseInt(thatMonth, 10) - 1;
            this.year = parseInt(thatYear, 10);
          }
        } else if (this.handleMultiDay.length) {
          this.month = parseInt(handleMultiDay[handleMultiDay.length - 1][1], 10) - 1;
          this.year = parseInt(handleMultiDay[handleMultiDay.length - 1][0], 10);
          this.handleMultiDay = [];
        } else {
          this.month = parseInt(this.value[this.value.length - 1][1], 10) - 1;
          this.year = parseInt(this.value[this.value.length - 1][0], 10);
        }
        this.day = parseInt((this.value[0] || [])[2], 10);
      } else {
        this.year = parseInt(this.value[0], 10);
        this.month = parseInt(this.value[1], 10) - 1;
        this.day = parseInt(this.value[2], 10);
      }
    }
    this.updateHeadMonth();
    if (this.isRendeRangeMode()) return;
    this.initRenderParam(this.year, this.month);
  }

  /**
   * @description 组件销毁
   * @memberof AppCalendar
   */
  unmounted() {
    if (isBrowser) {
      window.removeEventListener('resize', this.resize);
    }
  }

  /**
   * @description 创建初始化
   * @memberof AppCalendar
   */
  createdinit() {
    this.changebtntop = false;
    this.initRenderParam(this.year, this.month);
  }

  /**
   * @description 改变样式
   * @param {*} item 是否改变按钮
   * @memberof AppCalendar
   */
  changeStyle2(item: any) {
    this.changebtntop = item;
    this.initRenderParam(this.year, this.month);
  }

  /**
   * @description 绘制范围模型
   * @param {*} [renderer] 绘制参数
   * @return {*}
   * @memberof AppCalendar
   */
  isRendeRangeMode(renderer?: any) {
    this.isMonthRange = !!this.monthRange.length;
    if (this.isMonthRange) {
      this.initRendeRange(renderer);
      return true;
    }
  }

  /**
   * @description 初始化绘制范围
   * @param {*} [renderer] 绘制参数
   * @memberof AppCalendar
   */
  initRendeRange(renderer?: any) {
    const range: any[] = [];
    const monthRange = this.monthRange;
    const formatDateText = (fYear: any, fMonth: any) => {
      const reg = /([y]+)(.*?)([M]+)(.*?)$/i;
      const rangeMonthFormat = this.rangeMonthFormat || 'yyyy-MM';
      reg.exec(rangeMonthFormat);
      return (
        String(fYear).substring(4 - RegExp.$1.length) +
        RegExp.$2 +
        String(fMonth).substring(2 - RegExp.$3.length) +
        RegExp.$4
      );
    };
    if (monthRange[0] === monthRange[1]) {
      const [y, m] = monthRange[0].split('-');
      range.push([Number(y), Number(m), formatDateText(y, m)]);
    } else {
      const monthRangeOfStart = monthRange[0].split('-');
      const monthRangeOfEnd = monthRange[1].split('-');
      let startYear = +monthRangeOfStart[0];
      let startMonth = +monthRangeOfStart[1];
      const endYear = +monthRangeOfEnd[0];
      const endtMonth = +monthRangeOfEnd[1] > 12 ? 12 : +monthRangeOfEnd[1];
      while (startYear < endYear || startMonth <= endtMonth) {
        range.push([startYear, startMonth, formatDateText(startYear, startMonth)]);
        if (startMonth === 12 && startYear !== endYear) {
          startYear++;
          startMonth = 0;
        }
        startMonth++;
      }
    }
    this.rangeOfMonths = range;
    const monthsRange = range.map(item => {
      const [yearParam, monthParam] = item;
      return this.initRenderParam(yearParam, (monthParam as number) - 1, renderer);
    });
    this.monthRangeDays = monthsRange;
  }

  /**
   * @description 初始化绘制参数
   * @param {*} y 年
   * @param {*} m 月
   * @param {*} [renderer] 绘制参数
   * @param {*} [payload] 绘制参数类型
   * @return {*}
   * @memberof AppCalendar
   */
  initRenderParam(y: any, m: any, renderer?: any, payload?: any) {
    const weekSwitch = this.weekSwitch;
    const isCustomRender = renderer === 'CUSTOMRENDER';
    const isWatchRenderValue = renderer === '_WATCHRENDERVALUE_';
    this.year = y;
    this.month = m;
    if (renderer === '_WATCHRENDER_') return this.watchRender(payload);
    if (this.range && isWatchRenderValue) {
      if (!Array.isArray((this.value || [])[0])) {
        this.rangeBegin = [];
        this.rangeEnd = [];
      } else {
        this.rangeBegin = [this.value[0][0], this.value[0][1] - 1, this.value[0][2]];
        this.rangeEnd = [this.value[1][0], this.value[1][1] - 1, this.value[1][2]];
      }
    }
    if (isWatchRenderValue && weekSwitch) {
      this.positionWeek = true;
    }
    if (isCustomRender) {
      this.year = y;
      this.month = m;
      this.positionWeek = true;
      if (weekSwitch && !payload) {
        this.startWeekIndex = 0;
        this.weekIndex = 0;
      }
      this.updateHeadMonth();
    }
    const firstDayOfMonth = new Date(y, m, 1).getDay();
    const lastDateOfMonth = new Date(y, m + 1, 0).getDate();
    const lastDayOfLastMonth = new Date(y, m, 0).getDate();
    this.year = y;
    let i = 1;
    let line = 0;
    let nextMonthPushDays = 1;
    const temp: any[] = [];
    for (i; i <= lastDateOfMonth; i++) {
      const day = new Date(y, m, i).getDay();
      let k;
      if (day === 0) {
        temp[line] = [];
      } else if (i === 1) {
        temp[line] = [];
        k = lastDayOfLastMonth - firstDayOfMonth + 1;
        for (let j = 0; j < firstDayOfMonth; j++) {
          //generate prev month surplus option
          temp[line].push(
            Object.assign(
              this.renderOption(this.computedPrevYear(y, m), this.computedPrevMonth(false, m), k, 'prevMonth'),
              { lastMonth: true },
            ),
          );
          k++;
        }
      }
      temp[line].push(this.renderOption(y, m, i)); //generate current month option
      if (day === 6 && i < lastDateOfMonth) {
        line++;
      } else if (i === lastDateOfMonth) {
        let nextDay = 1;
        const lastDateOfMonthLength = this.monFirst ? 7 : 6;
        for (let d = day; d < lastDateOfMonthLength; d++) {
          //generate next month surplus option
          temp[line].push(
            Object.assign(
              this.renderOption(this.computedNextYear(y, m), this.computedNextMonth(false, m), nextDay, 'nextMonth'),
              { nextMonth: true },
            ),
          );
          nextDay++;
        }
        nextMonthPushDays = nextDay;
      }
      this.forceUpdate();
    }
    const completion = this.completion;
    if (this.monFirst) {
      if (!firstDayOfMonth) {
        let lastMonthDay = lastDayOfLastMonth;
        const LastMonthItems: any[] = [];
        for (let d = 1; d <= 7; d++) {
          LastMonthItems.unshift(
            Object.assign(
              this.renderOption(
                this.computedPrevYear(y, m),
                this.computedPrevMonth(false, m),
                lastMonthDay,
                'prevMonth',
              ),
              { lastMonth: true },
            ),
          );
          lastMonthDay--;
        }
        temp.unshift(LastMonthItems);
      }
      temp.forEach((item, index) => {
        if (!index) {
          return item.splice(0, 1);
        }
        temp[index - 1].length < 7 && temp[index - 1].push(item.splice(0, 1)[0]);
      });
      if (this.isMonthRange && temp[temp.length - 1][0].nextMonth) {
        temp.splice(temp.length - 1, 1); //if the first day of last line is nextMonth, delete this line
      }
      if (!completion && !weekSwitch) {
        const lastIndex = temp.length - 1;
        const secondToLastIndex = lastIndex - 1;
        const differentMonth = temp[lastIndex][0].date.split('-')[1] !== temp[secondToLastIndex][6].date.split('-')[1];
        differentMonth && temp.splice(lastIndex, 1);
      }
    }
    if (completion && !weekSwitch && temp.length <= 5 && nextMonthPushDays > 0) {
      for (let completionIndex = temp.length; completionIndex <= 5; completionIndex++) {
        temp[completionIndex] = [];
        const start = nextMonthPushDays + (completionIndex - line - 1) * 7;
        for (let d = start; d <= start + 6; d++) {
          temp[completionIndex].push(
            Object.assign(
              { day: d, disabled: true, nextMonth: true },
              this.getLunarInfo(this.computedNextYear(y, m), this.computedNextMonth(true, m), d),
              this.getEvents(this.computedNextYear(y, m), this.computedNextMonth(true, m), d),
            ),
          );
        }
      }
    }
    if (this.tileContent.length) {
      temp.forEach((item, index) => {
        item.forEach((v: any) => {
          const contents = this.tileContent.find(val => val.date === v.date);
          if (contents) {
            const { className, content } = contents || {};
            v.className = className;
            v.content = content;
          }
        });
      });
    }
    if (weekSwitch) {
      const tempLength = temp.length;
      const lastLineMonth = temp[tempLength - 1][0].date.split('-')[1]; // last line month
      const secondLastMonth = temp[tempLength - 2][0].date.split('-')[1]; // second-to-last line month
      lastLineMonth !== secondLastMonth && temp.splice(tempLength - 1, 1);
    }
    this.monthDays = temp;
    if (weekSwitch && !this.isMonthRange) {
      if (this.positionWeek) {
        let payloadDay = '';
        let searchIndex = true;
        if (Array.isArray(payload)) {
          //range
          payloadDay = [payload[0], payload[1] + 1, payload[2]].join('-');
        } else if (this.multi || isWatchRenderValue) {
          if (this.thisTimeSelect) {
            payloadDay = this.thisTimeSelect.join('-');
          } else {
            payloadDay = this.multi ? this.value[this.value.length - 1].join('-') : this.value.join('-');
          }
        }
        if (payload === 'SETTODAY') {
          payloadDay = todayString;
        } else if (isCustomRender) {
          if (typeof payload === 'string') {
            payloadDay = [y, Number(m) + 1, payload].join('-');
            searchIndex = true;
          } else if (typeof payload === 'number') {
            const setIndex = payload > temp.length ? temp.length - 1 : payload;
            this.startWeekIndex = setIndex;
            this.weekIndex = setIndex;
            this.positionWeek = false;
            searchIndex = false;
          }
        }
        const positionDay = payloadDay || todayString;
        if (searchIndex) {
          temp.some((v, index) => {
            const isWeekNow = v.find((vv: any) => vv.date === positionDay);
            if (isWeekNow) {
              this.startWeekIndex = index;
              this.weekIndex = index;
              return true;
            }
          });
        }
        this.positionWeek = false;
      }
      this.days = [temp[this.startWeekIndex]];
      if (this.changebtntop) {
        const days = temp.filter(item => {
          return (
            item.some((day: any) => {
              return day.selected == true;
            }) == true
          );
        });
        if (days.length > 0) {
          this.days = days;
        }
      }
      if (this.initRender) {
        this.setMonthRangeofWeekSwitch();
        this.initRender = false;
      }
    } else {
      this.days = temp;
    }
    const todayText = `${this.$tl('common.calendar.today', '今')}`;
    if (typeof this.now === 'boolean' && !this.now) {
      this.showToday = { show: false };
    } else if (typeof this.now === 'string') {
      this.showToday = {
        show: true,
        text: this.now || todayText,
      };
    } else {
      this.showToday = {
        show: true,
        text: todayText,
      };
    }
    this.monthRangeDays = [this.days];
    isWatchRenderValue && this.updateHeadMonth();
    return this.days;
  }

  /**
   * @description 处理绘制参数
   * @param {*} year 年
   * @param {*} month 月
   * @param {*} i 绘制参数
   * @param {*} [playload] 绘制参数类型
   * @return {*}
   * @memberof AppCalendar
   */
  renderOption(year: any, month: any, i: any, playload?: any): any {
    const weekSwitch = this.monthRange.length ? false : this.weekSwitch;
    const selectSplit = this.value;
    const isMonthModeCurrentMonth = !weekSwitch && !playload;
    const disabledFilter = (disabled: any) =>
      disabled.find((v: any) => {
        const dayArr = v.split('-');
        return year === Number(dayArr[0]) && month === dayArr[1] - 1 && i === Number(dayArr[2]);
      });
    if (this.range) {
      const lastDay = new Date(year, month + 1, 0).getDate() === i ? { lastDay: true } : null;
      const options = Object.assign(
        { day: i },
        this.getLunarInfo(year, month + 1, i),
        this.getEvents(year, month + 1, i),
        lastDay,
      );
      const { date, day } = options;
      const copyRangeBegin = this.rangeBegin.concat();
      const copyRangeEnd = this.rangeEnd.concat();
      copyRangeBegin[1] += 1;
      copyRangeEnd[1] += 1;
      if (weekSwitch || isMonthModeCurrentMonth) {
        copyRangeEnd.join('-') === date && ((options as any).rangeClassName = 'range-end');
        copyRangeBegin.join('-') === date && ((options as any).rangeClassName = 'range-begin');
      }
      if (year === copyRangeEnd[0] && month + 1 === copyRangeEnd[1] && day === copyRangeEnd[2] - 1) {
        (options as any).rangeClassName = (options as any).rangeClassName
          ? ['range-begin', 'range-second-to-last']
          : 'range-second-to-last';
      }
      if (this.rangeBegin.length) {
        const beginTime = +new Date(this.rangeBegin[0], this.rangeBegin[1], this.rangeBegin[2]);
        const endTime = +new Date(this.rangeEnd[0], this.rangeEnd[1], this.rangeEnd[2]);
        const stepTime = +new Date(year, month, i);
        if (beginTime <= stepTime && endTime >= stepTime) {
          (options as any).selected = true;
        }
      }
      if (this.begin.length) {
        const beginTime = +new Date(
          parseInt(this.begin[0], 10),
          parseInt(this.begin[1], 10) - 1,
          parseInt(this.begin[2], 10),
        );
        if (beginTime > +new Date(year, month, i)) {
          (options as any).disabled = true;
        }
      }
      if (this.end.length) {
        const endTime = Number(
          new Date(parseInt(this.end[0], 10), parseInt(this.end[1], 10) - 1, parseInt(this.end[2], 10)),
        );
        if (endTime < Number(new Date(year, month, i))) {
          (options as any).disabled = true;
        }
      }
      if (playload && !weekSwitch) {
        (options as any).disabled = true;
      } else if (this.disabled.length && disabledFilter(this.disabled)) {
        (options as any).disabled = true;
      }
      const monthFirstDay = `${year}-${month + 1}-1`;
      const monthLastDay = `${year}-${month + 1}-${new Date(year, month + 1, 0).getDate()}`;
      monthFirstDay === date &&
        (options as any).selected &&
        !(options as any).rangeClassName &&
        ((options as any).rangeClassName = 'range-month-first');
      monthLastDay === date &&
        (options as any).selected &&
        !(options as any).rangeClassName &&
        ((options as any).rangeClassName = 'range-month-last');
      this.isCurrentMonthToday(options) && ((options as any).isToday = true);
      !weekSwitch && playload && ((options as any).selected = false);
      return options;
    }
    if (this.multi) {
      let options;
      if (this.value.find((v: any) => year === v[0] && month === v[1] - 1 && i === v[2])) {
        options = Object.assign(
          { day: i, selected: true },
          this.getLunarInfo(year, month + 1, i),
          this.getEvents(year, month + 1, i),
        );
      } else {
        options = Object.assign(
          { day: i, selected: false },
          this.getLunarInfo(year, month + 1, i),
          this.getEvents(year, month + 1, i),
        );
        if (this.begin.length) {
          const beginTime = +new Date(
            parseInt(this.begin[0], 10),
            parseInt(this.begin[1], 10) - 1,
            parseInt(this.begin[2], 10),
          );
          if (beginTime > +new Date(year, month, i)) {
            (options as any).disabled = true;
          }
        }
        if (this.end.length) {
          const endTime = +new Date(
            parseInt(this.end[0], 10),
            parseInt(this.end[1], 10) - 1,
            parseInt(this.end[2], 10),
          );
          if (endTime < +new Date(year, month, i)) {
            (options as any).disabled = true;
          }
        }
        if (this.disabled.length && disabledFilter(this.disabled)) {
          (options as any).disabled = true;
        }
      }
      this.isCurrentMonthToday(options) && ((options as any).isToday = true);
      if (playload && !weekSwitch) {
        (options as any).disabled = true;
        (options as any).selected = false;
      }
      return options;
    } else {
      const options = {};
      const monthHuman = month + 1;
      if (selectSplit[0] === year && selectSplit[1] === monthHuman && selectSplit[2] === i) {
        Object.assign(
          options,
          { day: i, selected: true },
          this.getLunarInfo(year, monthHuman, i),
          this.getEvents(year, monthHuman, i),
        );
      } else {
        Object.assign(
          options,
          { day: i, selected: false },
          this.getLunarInfo(year, monthHuman, i),
          this.getEvents(year, monthHuman, i),
        );
        if (this.begin.length) {
          const beginTime = +new Date(
            parseInt(this.begin[0], 10),
            parseInt(this.begin[1], 10) - 1,
            parseInt(this.begin[2], 10),
          );
          if (beginTime > Number(new Date(year, month, i))) {
            (options as any).disabled = true;
          }
        }
        if (this.end.length) {
          const endTime = +new Date(
            parseInt(this.end[0], 10),
            parseInt(this.end[1], 10) - 1,
            parseInt(this.end[2], 10),
          );
          if (endTime < +new Date(year, month, i)) {
            (options as any).disabled = true;
          }
        }
        if (this.disabled.length && disabledFilter(this.disabled)) {
          (options as any).disabled = true;
        }
      }
      this.isCurrentMonthToday(options) && ((options as any).isToday = true);
      if (playload && !weekSwitch) {
        (options as any).disabled = true;
        (options as any).selected = false;
      }
      return options;
    }
  }

  /**
   * @description 是否为本月当天
   * @param {IParam} options 参数
   * @return {*}
   * @memberof AppCalendar
   */
  isCurrentMonthToday(options: IParam) {
    const isToday = todayString === options.date;
    if (!isToday) return false;
    return this.weekSwitch ? isToday : Number(todayString.split('-')[1]) === this.month + 1;
  }

  /**
   * @description 监听绘制变化
   * @param {string} type 绘制参数类型
   * @memberof AppCalendar
   */
  watchRender(type: string) {
    const weekSwitch = this.weekSwitch;
    const daysDeepCopy = Util.deepCopy(this.monthDays);
    if (type === 'events') {
      const events = this.events || {};
      Object.keys(events).forEach(value => {
        daysDeepCopy.some((v: any) =>
          v.some((vv: any) => {
            if (vv.date === value) {
              vv.eventName = events[value];
              return true;
            }
          }),
        );
      });
    } else if (type === 'disabled') {
      const disabled = this.disabled || [];
      disabled.forEach((value: any) => {
        daysDeepCopy.some((v: any) =>
          v.some((vv: any) => {
            if (vv.date === value) {
              vv.disabled = true;
              return true;
            }
          }),
        );
      });
    } else if (type === 'almanacs') {
      const almanacs = this.almanacs || {};
      Object.keys(almanacs).forEach((value: any) => {
        daysDeepCopy.some((v: any) =>
          v.some((vv: any) => {
            if (vv.date.slice(5, 20) === value) {
              const [y, m, d] = vv.date.split('-');
              Object.assign(vv, this.getLunarInfo(y, m, d));
              return true;
            }
          }),
        );
      });
    } else if (type === 'tileContent') {
      const tileContent = this.tileContent || [];
      tileContent.forEach((value: any) => {
        daysDeepCopy.some((v: any) =>
          v.some((vv: any) => {
            if (vv.date === value.date) {
              vv.className = value.className;
              vv.content = value.content;
              return true;
            }
          }),
        );
      });
    }
    this.monthDays = daysDeepCopy;
    if (weekSwitch) {
      this.days = [daysDeepCopy[this.weekIndex]];
      this.monthRangeDays = [this.days];
    } else {
      this.days = daysDeepCopy;
      this.monthRangeDays = [this.days];
    }
  }

  /**
   * @description 项样式
   * @return {*}
   * @memberof AppCalendar
   */
  itemStyle() {
    return {
      width: `${this.itemWidth}px`,
      height: `${this.itemWidth}px`,
      fontSize: `${this.itemWidth / 4}px`,
      lineHeight: this.lunar ? `${this.itemWidth / 1.5}px` : `${this.itemWidth}px`,
    };
  }

  /**
   * @description 行样式
   * @param {*} day 天
   * @param {*} days 天集合
   * @return {*}
   * @memberof AppCalendar
   */
  itemtrStyle(day: any, days: any[]) {
    const tempH = parseFloat(this.itemWidth.toString()) + 8;
    let tmpSelect = false;
    days.forEach((item: any) => {
      if (
        item.some((i: any) => {
          return i.selected;
        })
      ) {
        tmpSelect = true;
      }
    });
    const falg = day.some((item: any) => {
      return tmpSelect ? item.selected : item.isToday;
    });
    if (falg) {
      return { height: `${tempH}px` };
    }
    return this.changebtntop
      ? {
          height: `${tempH}px`,
        }
      : { height: 0 };
  }

  /**
   * @description 主体内容区样式
   * @return {*}
   * @memberof AppCalendar
   */
  mcbodyStyle() {
    let count = 0;
    this.monthRangeDays.forEach(item => {
      item.forEach((i: any) => {
        count++;
      });
    });
    if (!this.changebtntop) {
      count = 1;
    }
    return { height: `${(this.itemWidth + 16) * count + 45}px` };
  }

  /**
   * @description 临时接触长度
   * @return {*}
   * @memberof AppCalendar
   */
  tempTouchLength() {
    return this.touchLength > 0 ? this.touchLength : '';
  }

  /**
   * @description 改变样式
   * @memberof AppCalendar
   */
  changeStyle() {
    this.changebtntop = !this.changebtntop;
    this.initRenderParam(this.year, this.month);
  }

  /**
   * @description 处理绘制参数
   * @param {*} y 年
   * @param {*} m 月
   * @param {*} w 绘制参数类型
   * @memberof AppCalendar
   */
  renderer(y: any, m: any, w: any) {
    const renderY = y || this.year;
    const renderM = typeof parseInt(m, 10) === 'number' ? m - 1 : this.month;
    this.initRender = true;
    this.initRenderParam(renderY, renderM, 'CUSTOMRENDER', w);
    !this.weekSwitch && (this.monthsLoop = this.monthsLoopCopy.concat());
  }

  /**
   * @description 计算上一年
   * @param {number} year 年
   * @param {number} month 月
   * @return {*}
   * @memberof AppCalendar
   */
  computedPrevYear(year: number, month: number) {
    let value = year;
    if (month - 1 < 0) {
      value--;
    }
    return value;
  }

  /**
   * @description 计算上一月
   * @param {*} isString 是否+1
   * @param {number} month 月份
   * @return {*}
   * @memberof AppCalendar
   */
  computedPrevMonth(isString: boolean, month: number) {
    let value = month;
    if (month - 1 < 0) {
      value = 11;
    } else {
      value--;
    }
    if (isString) {
      return value + 1;
    }
    return value;
  }

  /**
   * @description 计算下一年
   * @param {number} year 年
   * @param {number} month 月
   * @return {*}
   * @memberof AppCalendar
   */
  computedNextYear(year: number, month: number) {
    let value = year;
    if (month + 1 > 11) {
      value++;
    }
    return value;
  }

  /**
   * @description 计算下一月
   * @param {*} isString 是否+1
   * @param {number} month 月
   * @return {*}
   * @memberof AppCalendar
   */
  computedNextMonth(isString: any, month: number) {
    let value = month;
    if (month + 1 > 11) {
      value = 0;
    } else {
      value++;
    }
    if (isString) {
      return value + 1;
    }
    return value;
  }

  /**
   * @description 获取阴历信息
   * @param {number} y 年
   * @param {number} m 月
   * @param {number} d 日
   * @return {*}
   * @memberof AppCalendar
   */
  getLunarInfo(y: number, m: number, d: number) {
    const lunarInfo: any = calendar.solar2lunar(y, m, d);
    const { Term, lMonth, lDay, lYear } = lunarInfo;
    let yearEve = '';
    if (lMonth === 12 && lDay === calendar.monthDays(lYear, 12)) {
      yearEve = '除夕';
    }
    let lunarValue = lunarInfo.IDayCn;
    let isLunarFestival = false;
    let isGregorianFestival = false;
    if (this.festival.lunar[`${lunarInfo.lMonth}-${lunarInfo.lDay}`]) {
      lunarValue = this.festival.lunar[`${lunarInfo.lMonth}-${lunarInfo.lDay}`];
      isLunarFestival = true;
    } else if (this.festival.gregorian[`${m}-${d}`]) {
      lunarValue = this.festival.gregorian[`${m}-${d}`];
      isGregorianFestival = true;
    }
    const lunarInfoObj = {
      date: `${y}-${m}-${d}`,
      lunar: yearEve || Term || lunarValue,
      isLunarFestival,
      isGregorianFestival,
      isTerm: !!yearEve || lunarInfo.isTerm,
    };
    if (Object.keys(this.almanacs).length) {
      Object.assign(lunarInfoObj, {
        almanac: this.almanacs[`${m}-${d}`] || '',
        isAlmanac: !!this.almanacs[`${m}-${d}`],
      });
    }
    return lunarInfoObj;
  }

  /**
   * @description 获取日程
   * @param {*} y 年
   * @param {*} m 月
   * @param {*} d 日
   * @return {*}
   * @memberof AppCalendar
   */
  getEvents(y: number, m: number, d: number) {
    if (!Object.keys(this.events).length) return;
    const eventName = this.events[`${y}-${m}-${d}`];
    const data: any = {};
    if (eventName) {
      data.eventName = eventName;
    }
    return data;
  }

  /**
   * @description 上一个
   * @param {MouseEvent} e 事件源
   * @return {*}
   * @memberof AppCalendar
   */
  prev(e: MouseEvent) {
    e && e.stopPropagation();
    if (this.isMonthRange) return;
    const weekSwitch = this.weekSwitch;
    const changeMonth = (changed?: any) => {
      if (this.monthIndex === 1) {
        this.oversliding = false;
        this.month = 11;
        this.year = parseInt(this.year.toString(), 10) - 1;
        this.monthIndex = this.monthIndex - 1;
      } else if (this.monthIndex === 0) {
        this.oversliding = true;
        this.monthIndex = 12;
        setTimeout(() => this.prev(e), 50);
        return this.updateHeadMonth('custom');
      } else if (this.monthIndex === 13) {
        this.month = 11;
        this.year = parseInt(this.year.toString(), 10) - 1;
        this.monthIndex = this.monthIndex - 1;
      } else {
        this.oversliding = false;
        this.month = parseInt(this.month.toString(), 10) - 1;
        this.monthIndex = this.monthIndex - 1;
      }
      this.updateHeadMonth('custom');
      this.initRenderParam(this.year, this.month);
      typeof changed === 'function' && changed();
      const weekIndex = weekSwitch ? this.weekIndex : undefined;
      this.resetSelected();
      this.ctx.emit('prev', this.year, this.month + 1, weekIndex);
    };
    if (!this.weekSwitch) return changeMonth();
    const changeWeek = () => {
      this.weekIndex = this.weekIndex - 1;
      this.days = [this.monthDays[this.weekIndex]];
      this.monthRangeDays = [this.days];
      this.setMonthRangeofWeekSwitch();
      this.resetSelected();
      this.ctx.emit('prev', this.year, this.month + 1, this.weekIndex);
    };
    const currentWeek = (this.days[0] || [])[0] || {};
    if (currentWeek.lastMonth || currentWeek.day === 1) {
      const monthChenged = () => {
        const lastMonthLength = this.monthDays.length;
        const startWeekIndex = currentWeek.lastMonth ? lastMonthLength - 1 : lastMonthLength;
        this.startWeekIndex = startWeekIndex;
        this.weekIndex = startWeekIndex;
        changeWeek();
      };
      changeMonth(monthChenged);
    } else {
      changeWeek();
    }
  }

  /**
   * @description 下一个
   * @param {MouseEvent} e 事件源
   * @return {*}
   * @memberof AppCalendar
   */
  next(e: MouseEvent) {
    e && e.stopPropagation();
    if (this.isMonthRange) return;
    const weekSwitch = this.weekSwitch;
    const changeMonth = () => {
      if (this.monthIndex === 12) {
        this.oversliding = false;
        this.month = 0;
        this.year = parseInt(this.year.toString(), 10) + 1;
        this.monthIndex = this.monthIndex + 1;
      } else if (this.monthIndex === 0 && this.month === 11) {
        this.oversliding = false;
        this.month = 0;
        this.year = parseInt(this.year.toString(), 10) + 1;
        this.monthIndex = this.monthIndex + 1;
      } else if (this.monthIndex === 13) {
        this.oversliding = true;
        this.monthIndex = 1;
        setTimeout(() => this.next(e), 50);
        return this.updateHeadMonth('custom');
      } else {
        this.oversliding = false;
        this.month = parseInt(this.month.toString(), 10) + 1;
        this.monthIndex = this.monthIndex + 1;
      }
      this.updateHeadMonth('custom');
      this.initRenderParam(this.year, this.month);
      const weekIndex = weekSwitch ? this.weekIndex : undefined;
      this.resetSelected();
      this.ctx.emit('next', this.year, this.month + 1, weekIndex);
    };
    if (!this.weekSwitch) return changeMonth();
    const changeWeek = () => {
      this.weekIndex = this.weekIndex + 1;
      this.days = [this.monthDays[this.weekIndex]];
      this.monthRangeDays = [this.days];
      this.setMonthRangeofWeekSwitch();
      this.resetSelected();
      this.ctx.emit('next', this.year, this.month + 1, this.weekIndex);
    };
    const currentWeek = (this.days[0] || [])[6] || {};
    if (currentWeek.nextMonth || currentWeek.day === new Date(this.year, this.month + 1, 0).getDate()) {
      const startWeekIndex = currentWeek.nextMonth ? 1 : 0;
      this.startWeekIndex = startWeekIndex;
      this.weekIndex = startWeekIndex;
      changeMonth();
    } else {
      changeWeek();
    }
  }

  /**
   * @description 选中日期
   * @param {number} k1 下标1
   * @param {number} k2 下标2
   * @param {IParam} data 数据
   * @param {MouseEvent} e 事件源
   * @param {number} monthIndex 周索引
   * @return {*}
   * @memberof AppCalendar
   */
  select(k1: number, k2: number, data: IParam, e: MouseEvent, monthIndex: number) {
    e && e.stopPropagation();
    const weekSwitch = this.weekSwitch;
    if (data.lastMonth && !weekSwitch) {
      return this.prev(e);
    }
    if (data.nextMonth && !weekSwitch) {
      return this.next(e);
    }
    if (data.disabled) return;
    (data || {}).event = (this.events || {})[data.date] || '';
    const { selected, day, date } = data;
    const selectedDates = date.split('-');
    const selectYear = Number(selectedDates[0]);
    const selectMonth = selectedDates[1] - 1;
    const selectMonthHuman = Number(selectedDates[1]);
    const selectDay = Number(selectedDates[2]);
    if (this.range) {
      this.isUserSelect = true;
      const rangeDate = (dateArray: any) =>
        dateArray.map((v: any, k: number) => {
          const value = k === 1 ? v + 1 : v;
          return this.zero ? this.zeroPad(value) : value;
        });
      if (this.rangeBegin.length === 0 || this.rangeEndTemp !== 0) {
        this.rangeBegin = [selectYear, selectMonth, selectDay];
        this.rangeBeginTemp = this.rangeBegin;
        this.rangeEnd = [selectYear, selectMonth, selectDay];
        this.thisTimeSelect = this.rangeEnd;
        this.rangeEndTemp = 0;
        this.ctx.emit('select', rangeDate(this.rangeBegin), undefined);
      } else {
        this.rangeEnd = [selectYear, selectMonth, selectDay];
        this.thisTimeSelect = [selectYear, selectMonth, selectDay];
        if (this.rangeBegin.join('-') === this.rangeEnd.join('-')) {
          return (this.rangeEndTemp = 0);
        }
        this.rangeEndTemp = 1;
        if (
          +new Date(this.rangeEnd[0], this.rangeEnd[1], this.rangeEnd[2]) <
          +new Date(this.rangeBegin[0], this.rangeBegin[1], this.rangeBegin[2])
        ) {
          this.rangeBegin = this.rangeEnd;
          this.rangeEnd = this.rangeBeginTemp;
        }
        const begin = rangeDate(this.rangeBegin);
        const end = rangeDate(this.rangeEnd);
        this.value.splice(0, 1, begin);
        this.value.splice(1, 1, end);
        this.ctx.emit('select', begin, end);
      }
      this.rangeBgHide = !this.rangeEndTemp || this.rangeBegin.join('-') === this.rangeEnd.join('-');
      this.positionWeek = true;
      if (this.isMonthRange) {
        this.initRendeRange();
      } else {
        this.initRenderParam(this.year, this.month, undefined, this.thisTimeSelect);
      }
    } else if (this.multi) {
      this.isUserSelect = true;
      const filterDayIndex = this.value.findIndex((v: any) => v.join('-') === date);
      if (~filterDayIndex) {
        this.handleMultiDay = this.value.splice(filterDayIndex, 1);
      } else {
        this.value.push([Number(Number(selectedDates[0])), Number(selectedDates[1]), day]);
      }
      this.monthRangeDays[monthIndex][k1][k2].selected = !selected;
      this.multiDaysData = this.value.map((dateItem: any) => {
        const [year, month, d] = dateItem;
        return Object.assign(
          { day: d, selected: true },
          this.getLunarInfo(year, month, d),
          this.getEvents(year, month, d),
        );
      });
      this.thisTimeSelect = date;
      this.ctx.emit('select', this.value, this.multiDaysData);
    } else {
      // 有改动
      const valueClone = this.value;
      const currentSelected = valueClone.join('-');
      this.monthRangeDays.some(value =>
        value.some(
          (v: any) =>
            !!v.find((vv: any) => {
              if (vv.date === currentSelected) {
                vv.selected = false;
                return true;
              }
            }),
        ),
      );
      this.monthRangeDays[monthIndex][k1][k2].selected = true;
      this.day = day;
      const selectDate = [selectYear, selectMonthHuman, selectDay];
      this.value[0] = selectYear;
      this.value[1] = selectMonthHuman;
      this.value[2] = selectDay;
      this.today = [k1, k2];
      this.ctx.emit('select', selectDate, data);
    }
  }

  /**
   * @description 重置选中
   * @memberof AppCalendar
   */
  resetSelected() {
    this.monthRangeDays.forEach((days: any) => {
      days.forEach((day: any) => {
        day.forEach((child: any) => {
          child.selected = false;
        });
      });
    });
  }

  /**
   * @description 改变年
   * @return {*}
   * @memberof AppCalendar
   */
  changeYear() {
    if (this.yearsShow) {
      this.yearsShow = false;
      return false;
    }
    this.yearsShow = true;
    this.years = [];
    for (let i = this.year - 5; i < this.year + 7; i++) {
      this.years.push(i);
    }
  }

  /**
   * @description 改变月
   * @param {number} value 月
   * @memberof AppCalendar
   */
  changeMonth(value: number) {
    this.oversliding && (this.oversliding = false);
    this.yearsShow = false;
    this.month = value;
    this.initRenderParam(this.year, this.month, 'CUSTOMRENDER', 0);
    this.updateHeadMonth();
    this.weekSwitch && this.setMonthRangeofWeekSwitch();
    this.ctx.emit('selectMonth', this.month + 1, this.year);
  }

  /**
   * @description 选择年
   * @param {number} value 年
   * @memberof AppCalendar
   */
  selectYear(value: number) {
    this.yearsShow = false;
    this.year = value;
    this.initRenderParam(this.year, this.month);
    this.ctx.emit('selectYear', value);
  }

  /**
   * @description 设置今天
   * @memberof AppCalendar
   */
  setToday() {
    const now = new Date();
    this.year = now.getFullYear();
    this.month = now.getMonth();
    this.day = now.getDate();
    this.positionWeek = true;
    this.initRenderParam(this.year, this.month, undefined, 'SETTODAY');
    this.updateHeadMonth();
  }

  /**
   * @description 设置月范围和周开关
   * @memberof AppCalendar
   */
  setMonthRangeofWeekSwitch() {
    this.monthsLoop = this.monthsLoopCopy.concat();
    this.days[0].reduce((prev: any, current: any) => {
      if (!prev) return;
      const prveDate = ((prev || {}).date || '').split('-');
      const prevYear = prveDate[0];
      const prevMonth = prveDate[1];
      const currentMonth = ((current || {}).date || '').split('-')[1];
      if (prevMonth === currentMonth) {
        return current;
      }
      const prevMonthText = this.months[prevMonth - 1];
      const currentMonthText = this.months[currentMonth - 1];
      this.monthsLoop[this.monthIndex] = `${prevMonthText}~${currentMonthText}`;
    });
  }

  /**
   * @description 数据信息
   * @param {number} y 年
   * @param {number} m 月
   * @param {number} d 日
   * @return {*}
   * @memberof AppCalendar
   */
  dateInfo(y: number, m: number, d: number) {
    return calendar.solar2lunar(y, m, d);
  }

  /**
   * @description 补零
   * @param {number} n 需补零的数
   * @return {*}
   * @memberof AppCalendar
   */
  zeroPad(n: number) {
    return String(n < 10 ? `0${n}` : n);
  }

  /**
   * @description 更新头部显示月
   * @param {*} [type] 类型
   * @memberof AppCalendar
   */
  updateHeadMonth(type?: string) {
    if (!type) this.monthIndex = this.month + 1;
    this.monthPosition = this.monthIndex * this.positionH;
    this.monthText = this.months[this.month];
  }

  /**
   * @description 添加监听事件
   * @memberof AppCalendar
   */
  addResponsiveListener() {
    window.addEventListener('resize', this.resize);
  }

  /**
   * @description 改变大小
   * @memberof AppCalendar
   */
  resize() {
    this.itemWidth = Number((document.documentElement.clientWidth / 7 - 4).toFixed(5));
  }

  /**
   * @description 绘制日历操作栏
   * @return {*}
   * @memberof AppCalendar
   */
  renderCalendarTool() {
    if (!this.isMonthRange) {
      return (
        <div class='calendar-tools'>
          <div class='calendar-prev' onClick={(e: any) => this.prev(e)}>
            {this.arrowLeft ? <img src={this.arrowLeft} /> : <i class='iconfont icon-arrow-left'></i>}
          </div>
          <div class='calendar-next' onClick={(e: any) => this.next(e)}>
            {this.arrowLeft ? <img src={this.arrowLeft} /> : <i class='iconfont icon-arrow-right'></i>}
          </div>
          <div class='calendar-info' onClick={(e: any) => this.changeYear()}>
            <div class='month'>
              {this.isIos ? (
                <div
                  class={['month-inner', this.oversliding ? '' : 'month-transition']}
                  style={{ top: this.monthPosition + this.unit }}
                >
                  {this.monthsLoop?.map((month: any) => {
                    return <span>{month}</span>;
                  })}
                </div>
              ) : (
                <div class='month-text'>{this.monthText}</div>
              )}
            </div>
            <div class='year'>{this.year}</div>
          </div>
        </div>
      );
    }
  }

  /**
   * @description 绘制日历内容区
   * @return {*}
   * @memberof AppCalendar
   */
  renderCalendarContent() {
    return (
      <table cellpadding='5'>
        <div class={['calendar-head', { 'month-range-mode-head': this.isMonthRange }]}>
          <div class='head-box'>
            {this.weeks?.map((week: any) => {
              return <div class='week'>{week}</div>;
            })}
          </div>
        </div>
        {this.monthRangeDays?.map((days: any, index: number) => {
          const style = {
            transition: 'all 0.5s ease-in-out 0s',
            overflow: 'hidden',
          };
          Object.assign(style, this.mcbodyStyle());
          return (
            <div
              class={[
                'calendar-body',
                {
                  'range-mode': this.range,
                  'week-switch': this.weekSwitch && !this.isMonthRange,
                  'month-range-mode': this.isMonthRange,
                },
              ]}
              style={style}
            >
              {this.isMonthRange && <div class='month-rang-head'>{this.rangeOfMonths[index][2]}</div>}
              {days.map((day: any, k1: number) => {
                const style = {
                  transition: 'all .5s',
                  overflow: 'hidden',
                };
                Object.assign(style, this.itemtrStyle(day, days));
                return (
                  <tr class={{ gregorianStyle: !this.lunar }} style={style}>
                    {day.map((child: any, k2: number) => {
                      return (
                        <td
                          class={[
                            {
                              selected: child.selected,
                              'today-element': child.isToday,
                              disabled: child.disabled,
                              day: true,
                              'range-select-one': this.rangeBgHide && child.selected,
                              lunarStyle: this.lunar,
                              'range-row-first': k2 === 0 && child.selected,
                              'month-last-date': child.lastDay,
                              'month-first-date': 1 === child.day,
                              'range-row-last': k2 === 6 && child.selected,
                              'last-month': child.lastMonth,
                              'next-month': child.nextMonth,
                            },
                            child.className,
                            child.rangeClassName,
                          ]}
                          onClick={(e: any) => this.select(k1, k2, child, e, index)}
                          style={this.itemStyle()}
                        >
                          {this.showToday?.show && child.isToday ? (
                            <span class='today calendar-date'>{this.showToday.text}</span>
                          ) : (
                            <span class={[{ 'date-red': k2 === (this.monFirst ? 5 : 0) || k2 === 6 }, 'calendar-date']}>
                              {child.day}
                            </span>
                          )}
                          {!!child.content && <div class='slot-element'>{child.content}</div>}
                          {child.eventName && !this.clean && <div class='text remark-text'>{child.eventName}</div>}
                          {child.eventName && this.clean && <div class='dot' />}
                          {this.sign?.map((usersign: any, us: number) => {
                            if (usersign.time == child.date) {
                              return (
                                <div class='sign'>
                                  {usersign.evens.map((sigin: any, si: any) => {
                                    return (
                                      <div class='sign-item' style={{ color: sigin.color }}>
                                        —
                                      </div>
                                    );
                                  })}
                                </div>
                              );
                            }
                          })}
                          {this.lunar && (!child.eventName || this.clean) && (
                            <div
                              class={{
                                text: true,
                                isLunarFestival: child.isAlmanac || child.isLunarFestival,
                                isGregorianFestival: child.isGregorianFestival,
                                isTerm: child.isTerm,
                              }}
                            >
                              {child.almanac || child.lunar}
                            </div>
                          )}
                          {this.range && child.selected && <div class='range-bg' />}
                        </td>
                      );
                    })}
                  </tr>
                );
              })}
            </div>
          );
        })}
      </table>
    );
  }

  /**
   * @description 绘制日历改变
   * @return {*}
   * @memberof AppCalendar
   */
  renderCalendarChange() {
    return (
      <div class={{ 'app-calendar-change': true, show: this.yearsShow }}>
        {!this.weekSwitch && (
          <div class='calendar-years'>
            {this.years?.map((year: number) => {
              return (
                <span onClick={() => this.selectYear(year)} class={{ active: year === this.year }}>
                  {year}
                </span>
              );
            })}
          </div>
        )}
        <div class={['calendar-months', { 'calendar-week-switch-months': this.weekSwitch }]}>
          {this.months?.map((month: string, index: number) => {
            return (
              <span onClick={() => this.changeMonth(index)} class={{ active: index === this.month }}>
                {month}
              </span>
            );
          })}
        </div>
      </div>
    );
  }

  /**
   * @description 绘制用户定义
   * @return {*}
   * @memberof AppCalendar
   */
  renderCalendarUser() {
    const rendarUser: any[] = [];
    if (this.isChangeStyle) {
      const user1 = (
        <div onClick={() => this.changeStyle()} class='changebox'>
          {this.changebtntop ? <div class='changeStyle_top'></div> : <div class='changeStyle_bottom'></div>}
        </div>
      );
      rendarUser.push(user1);
    }
    if (this.illustration) {
      const user2 = (
        <div class='illustration'>
          {this.illustration.map((il: any) => {
            return (
              <div class='illustration-item'>
                <div style={{ backgroundColor: il.color }} class='illustration-color'></div>
                <div class='illustration-text'>{il.text}</div>
              </div>
            );
          })}
        </div>
      );
      rendarUser.push(user2);
    }
    return rendarUser;
  }

  /**
   * @description 绘制日历
   * @return {*}
   * @memberof AppCalendar
   */
  render() {
    return (
      <div class='app-calendar'>
        {this.renderCalendarTool()}
        {this.renderCalendarContent()}
        {this.renderCalendarChange()}
        {this.renderCalendarUser()}
      </div>
    );
  }
}

export const AppCalendarComponent = GenerateComponent(AppCalendar, Object.keys(new AppCalendarProps()));
